package org.richin.support.orm.hibernate;

import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.transform.Transformers;
import org.richin.array.util.ArrayUtils;
import org.richin.lang.util.Convert;
import org.richin.support.orm.DaoSupport;
import org.richin.support.orm.IHibernateDaoSupport;
import org.richin.support.orm.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springside.modules.orm.PropertyFilter;
import org.springside.modules.utils.ReflectionUtils;

@SuppressWarnings("unchecked")
public class HibernateDaoSupportImp<T, ID extends Serializable> implements DaoSupport<T, ID>,
		IHibernateDaoSupport<T, ID> {
	protected Logger logger = LoggerFactory.getLogger(super.getClass());
	protected SessionFactory sessionFactory;
	protected Class<T> entityClass;

	public HibernateDaoSupportImp() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}

	public HibernateDaoSupportImp(SessionFactory sessionFactory, Class<T> entityClass) {
		this.sessionFactory = sessionFactory;
		this.entityClass = entityClass;
	}

	public SessionFactory getSessionFactory() {
		return this.sessionFactory;
	}

	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public Session getSession() {
		return this.sessionFactory.getCurrentSession();
	}

	public void save(T entity) {
		Assert.notNull(entity);
		getSession().saveOrUpdate(entity);
		this.logger.debug("save entity: {}", entity);
	}

	public void delete(T entity) {
		Assert.notNull(entity);
		getSession().delete(entity);
		this.logger.debug("delete entity: {}", entity);
	}

	public void delete(ID...ids) {
		if(ArrayUtils.getLength(ids)>1){
			for(int i=0;i<ids.length;i++)
			removeById(ids[i]);
		}else
			this.delete(get(ArrayUtils.first(ids)));

	}
	  /**
     * 根据主键删除记录
     * @param id
     */
    public void removeById(ID id){
        Map map = getSessionFactory().getAllClassMetadata();
        SingleTableEntityPersister p = (SingleTableEntityPersister)map.get(entityClass.getName());
        String idName = p.getIdentifierPropertyName();
        String hql = "delete from "+entityClass.getName()+" where "+idName+"=?";
       createQuery(hql, id);
    }
	public T get(ID id) {
		Assert.notNull(id);
		return get(this.entityClass, id);
	}

	public T get(Class<T> entityClass, ID id) {
		return (T) getSession().get(entityClass, id);
	}

	public T load(ID id) {
		return (T) getSession().load(entityClass, id);
	}

	public List<T> getAll() {
		return findByCriteria(new Criterion[0]);
	}
	public List<T> getAll(boolean cacheQueries) {
		return findByCriteria(new Criterion[0], cacheQueries);
	}
	public List<T> findByHQL(String hql) {
		return getSession().createQuery(hql).list();
	}

	public List<T> findByHql(String hql, Object... values) {
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query.list();
	}
	public T findUniqueByNamedQuery(String queryName, Object... values){
		return (T)getNamedQuery(queryName, values).uniqueResult();
	}
	public T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(new Criterion[] { criterion }).uniqueResult();
	}

	public List<T> findByCriteria(Criterion[] criterions) {
		return createCriteria(criterions).list();
	}
	public List<T> findByCriteria(Criterion[] criterions,boolean cacheQueries) {
		return createCriteria(criterions).setCacheable(cacheQueries).list();
	}

	public List<T> findByCriteria(DetachedCriteria criteria, int firstResult, int maxResults) {
		return criteria.getExecutableCriteria(this.getSession()).setFirstResult(firstResult).setMaxResults(maxResults)
				.list();

	}

	public Criteria createCriteria(Criterion...criterions) {
		Criteria criteria = getSession().createCriteria(this.entityClass);
		Criterion[] arr$ = criterions;
		int len$ = arr$.length;
		for (int i$ = 0; i$ < len$; ++i$) {
			Criterion c = arr$[i$];
			criteria.add(c);
		}
		return criteria;
	}
	/**
	 * 根据entityClass生成对应类型的Criteria.
	 *
	 * @param entityClass 实体类型
	 * @param criterions 条件
	 * @return Criteria
	 */
	public Criteria createCriteria(Class entityClass, Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);

		for (Criterion c : criterions) {
			criteria.add(c);
		}

		return criteria;
	}
	public List<T> find(String hql, Object... values) {
		return createQuery(hql, values).list();
	}

	public Query createQuery(String queryString, Object...values) {
		int i;
		Assert.hasText(queryString);
		Query query = getSession().createQuery(queryString);
		if (values != null)
			for (i = 0; i < values.length; ++i)
				query.setParameter(i, values[i]);

		return query;
	}

	public List<T> findByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		Criterion criterion = Restrictions.eq(propertyName, value);
		return findByCriteria(new Criterion[] { criterion });
	}

	public Object findUnique(String hql, Object... values) {
		return createQuery(hql, values).uniqueResult();
	}

	public void update(T entity) {
		getSession().update(entity);

	}

	public List<T> getAll(Class<?> entityClass, String orderBy, boolean isAsc) {
		Assert.hasText(orderBy);
		DetachedCriteria criteria;
		if (isAsc)
			criteria=	DetachedCriteria.forClass(entityClass).addOrder(Order.asc(orderBy));
		else
			criteria= DetachedCriteria.forClass(entityClass).addOrder(Order.desc(orderBy));
		return criteria.getExecutableCriteria(getSession()).list();
	}

	public Iterator<T> iterate(String hql, Object... values) {
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query.iterate();
	}

	public void updateAll(List<T> entities) {
		Session session = getSession();
		Iterator<T> it = entities.iterator();
		while (it.hasNext())
			session.saveOrUpdate(it.next());
	}

	public void deleteAll(Collection<T> entities) {
		Session session = getSession();
		Iterator<T> iterator = entities.iterator();
		while (iterator.hasNext()) {
			session.delete(iterator.next());

		}
	}

	public boolean isNotUnique(T entity, String names) {
		Assert.hasText(names);
		Criteria criteria = createCriteria(entityClass).setProjection(Projections.rowCount());
		String[] nameList = names.split(",");
		try {
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
			}

			String idName = getIdName(entityClass);
			if (idName != null) {
				Object id = PropertyUtils.getProperty(entity, idName);
				// 如果是update,排除自身
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
			return false;
		}
		return Convert.castToNumber(criteria.uniqueResult()).intValue() > 0;
	}

	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(this.entityClass);
		Assert.notNull(meta, "Class " + this.entityClass.getSimpleName() + " not define in HibernateSessionFactory.");
		return meta.getIdentifierPropertyName();
	}

	public List<T> findByExample(T entity) {
		Criteria criteria = createCriteria();
		criteria.add(Example.create(entity));
		return criteria.list();
	}

	public List<T> findByNamedQuery(String queryName) {
		return getSession().getNamedQuery(queryName).list();
	}

	public List<T> findByNamedQuery(String queryName, String[] paramNames, Object[] values) {
		Query query = getSession().getNamedQuery(queryName);
		for (int i = 0; i < paramNames.length; i++) {
			query.setParameter(paramNames[i], values[i]);
		}
		return query.list();
	}

	public List<T> findByNamedQuery(String queryName, Object... values) {
		Query query = getSession().getNamedQuery(queryName);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query.list();
	}

	public void lock(T entity, LockMode lockMode) {
		getSession().lock(entity, lockMode);
	}

	public Object getStatValue(DetachedCriteria criteria, String propertyName, String StatName) {
		if (StatName.toLowerCase().equals("max"))
			criteria.setProjection(Projections.max(propertyName));
		else if (StatName.toLowerCase().equals("min"))
			criteria.setProjection(Projections.min(propertyName));
		else if (StatName.toLowerCase().equals("avg"))
			criteria.setProjection(Projections.avg(propertyName));
		else if (StatName.toLowerCase().equals("sum"))
			criteria.setProjection(Projections.sum(propertyName));
		else
			return null;
		List list = this.findByCriteria(criteria, 0, 1);
		return list.get(0);
	}

	public Integer getRowCount(DetachedCriteria criteria) {
		criteria.setProjection(Projections.rowCount());
		List list = this.findByCriteria(criteria, 0, 1);
		return (Integer) list.get(0);

	}

	public boolean isPropertyUnique(String propertyName, Object newValue, Object orgValue) {
		if ((newValue == null) || (newValue.equals(orgValue)))
			return true;
		Object object = findUniqueByProperty(propertyName, newValue);
		return (object == null);
	}

	public void deleteWithLock(T entity, LockMode lock) {
	}

	public int bulkUpdate(String queryString) {
		Session session = getSession();
		Query query = session.createQuery(queryString);
		return query.executeUpdate();
	}

	public int bulkUpdate(String queryString, Object... values) {
		Assert.noNullElements(values);
		Session session = getSession();
		Query query = session.createQuery(queryString);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query.executeUpdate();
	}

	// 创建与会话无关的检索标准
	public DetachedCriteria createDetachedCriteria() {
		return DetachedCriteria.forClass(this.entityClass);
	}

	// 创建与会话绑定的检索标准
	public Criteria createCriteria() {
		return this.createDetachedCriteria().getExecutableCriteria(this.getSession());
	}

	public T findByUnique(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = Restrictions.eq(propertyName, value);
		return (T) createCriteria(new Criterion[] { criterion }).uniqueResult();
	}

	protected Criterion buildPropertyCriterion(String propertyName, Object value, PropertyFilter.MatchType matchType) {
		Assert.hasText(propertyName, "propertyName不能为空");
		Criterion criterion = null;

		if (PropertyFilter.MatchType.EQ.equals(matchType))
			criterion = Restrictions.eq(propertyName, value);

		if (PropertyFilter.MatchType.LIKE.equals(matchType)) {
			criterion = Restrictions.like(propertyName, (String) value, MatchMode.ANYWHERE);
		}

		return criterion;
	}

	protected Query createSQLQuery(String queryString, Map<String, Object> values) {
		Assert.hasText(queryString);
		Query query = getSession().createSQLQuery(queryString);
		if (values != null)
			for (Map.Entry<String, Object> entry : values.entrySet())
				query.setParameter(entry.getKey(), entry.getValue());

		return query;
	}
	protected Query getNamedQuery(String queryName, Object... values){
		int i;
		Assert.hasText(queryName);
		Query query = getSession().getNamedQuery(queryName);
		if (values != null)
			for (i = 0; i < values.length; ++i)
				query.setParameter(i, values[i]);

		return query;
	}
	protected Query createSQLQuery(String queryString, Object...values) {
		int i;
		Assert.hasText(queryString);
		Query query = getSession().createSQLQuery(queryString);
		if (values != null)
			for (i = 1; i < values.length; ++i)
				query.setParameter(i, values[i - 1]);

		return query;
	}
	public Map<String, Object> findBySqlUniqueToMap(String queryString, Map<String, Object> values) {
		Query query = createSQLQuery(queryString, values);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return (Map<String, Object>) query.uniqueResult();
	}

	public int executeSql(String queryString, Map<String, Object> values) {
		Query query = createSQLQuery(queryString, values);
		return query.executeUpdate();
	}

	public int executeSql(String queryString, Object... values) {
		Query query = createSQLQuery(queryString, values);
		return query.executeUpdate();
	}

	public Map<String, Object> findBySqlUniqueToMap(String queryString, Object... values) {
		Query query = createSQLQuery(queryString, values);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return (Map<String, Object>) query.uniqueResult();
	}

	public Object findUniqueBySql(String sql, Object... values) {
		return createSQLQuery(sql, values).uniqueResult();
	}

	public Object findUniqueBySql(String sql) {
		return findUniqueBySql(sql, new Object[]{});
	}
	public Object findUniqueBySql(String sql, Map<String, Object> values) {
		return createSQLQuery(sql,values).uniqueResult();
	}
	public void saveOrUpdateAll(Collection<T> entities) {
		Session session=getSession();
		for (Iterator it = entities.iterator(); it.hasNext(); session.saveOrUpdate(it.next()))
			;
		
	}

	public List<Map<String, Object>> findBySqlToMapList(String queryString, Map<String, Object> values) {
		Query query = createSQLQuery(queryString, values);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}
	public List<Map<String, Object>> findBySqlToMapList(String queryString, Object... values) {
		Query query = createSQLQuery(queryString, values);
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return query.list();
	}
	public void deleteObject(Object entity) {
		Assert.notNull(entity);
		getSession().delete(entity);
		
	}

	public void saveObject(Object entity) {
		Assert.notNull(entity);
		getSession().save(entity);
		
	}

	public void updateObject(Object entity) {
		Assert.notNull(entity);
		getSession().update(entity);
		
	}

	public T findUniqueByHql(String queryString, Object... values) {
		return (T)createQuery(queryString, values).uniqueResult();
	}

	public String getIdName(Class<T> entityClass) {
	     Assert.notNull(entityClass);

	        ClassMetadata meta = getSessionFactory()
	                                 .getClassMetadata(entityClass);
	        Assert.notNull(meta,
	            "Class " + entityClass
	            + " not define in hibernate session factory.");

	        String idName = meta.getIdentifierPropertyName();
	        Assert.hasText(idName,
	            entityClass.getSimpleName()
	            + " has no identifier property define.");

	        return idName;
	}

	public Page<T> getAll(Page<T> page) {
		// TODO Auto-generated method stub
		return null;
	}

	public void saveAll(Collection<T> entities) {
		for (Iterator it = entities.iterator(); it.hasNext(); getSession().save(it.next()))
			;
	}
	public List<T> findByLike(String propertyName, String value) {
		return createCriteria(Restrictions.ilike(propertyName, value)).list();

	}





}
